iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Software Development

Laravel專案練習-寶可夢管理系統系列 第 17

Day17:寶可夢專案-寶可夢資料庫的建立-儲存在本地端的方法-如何發請求、解析數據、儲存

  • 分享至 

  • xImage
  •  

根據上一篇談到的資料取得策略, 我是選擇了在一開始發送大量請求並存在自己的資料庫, 那麼我接下來要做的事:

實際過程

1.實際發送的方法

首先我先取得所有寶可夢的名稱、照片、招式。

這裡我是先去看了所有寶可夢總共有幾隻,用for 迴圈去對這個數量做一個一個的請求發送。

這裡我故意留了幾個dd(), 說明我在大量發送前先做了屬性是否取得的確認。

  • 解析數據

    然後其實我覺得這裡數據的解析其實就是去看對方的API回傳的資料結構, 然後去拿到你想要的資料。比如這裡招式的部分, 我要這隻寶可夢可以學的所有招式的名稱, 但API返回的是招式的名稱還有威力等等…, 我就要去針對名稱的部分做拿取, 並存到陣列

    $batchSize = 1; // 可以根據需要調整
            $totalPokemons = 1011; // 根據你的實際數量調整
    
            for ($i = 1; $i < $totalPokemons; $i += $batchSize) {
                // 取得名稱、照片、招式
                $response1 = Http::get("https://pokeapi.co/api/v2/pokemon/$i");
                $pokemonDetail = $response1->json();
                $name = $pokemonDetail['name'];
                // dd($name);
                $photo = $pokemonDetail["sprites"]['front_default'];
                $moves = $pokemonDetail['moves'];
                $moveNames = [];
                // 取出此寶可夢所能學的所有的招式名稱
                foreach ($moves as $moves) {
                    $moveNames[] = $moves['move']['name'];
                    // dd($moveNames);
                }
    

    以下的不是針對進化等級的取的, 這裡其實不是我想多打一次API, 而是我想要的某些資料對方就是分成不同的API, 比如下面的進化等級。

    他返回的資料結構是比較複雜, 他是一個三維的結構, 我額外寫了一個函數去拿到資料,

    // 取得進化等級,這在pokemon-species的進化鏈裡面
                $pokemonData = Http::get("https://pokeapi.co/api/v2/pokemon-species/$i")->json();
                $getEvolutionChain = $pokemonData["evolution_chain"]['url'];
                $evolutionChain = Http::get($getEvolutionChain)->json();
                $minLevel = $this->getEvolutionMinLevel($evolutionChain, $name);
                // dd($minLevel);
    
    
    最後儲存的部分我主要都是存在Race表單, 但關於技能的部分, 我其實有一張Race表單,
    他是和技能表單多對多關聯,所以我是將技能存到這裡。
    						$race = Race::create([
                    'name' => $name,
                    'photo' => $photo,
                    'evolution_level' => $minLevel
                ]);
    
                foreach ($moveNames as $moveName) {
                    // 先找出或創建招式
                    $move = Skill::firstOrCreate(['name' => $moveName]);
    
                    // 關聯到寶可夢
                    $race->skills()->attach($move->id);
                }
    
    • 為何會使用for 迴圈這樣做?
      • 這裡是因為對方資料放的位置,讓我一定要去打兩支api才能拿到所有我想拿到的資訊。
      • 所以我想在一個循環裡面就把所有資料拿齊然後一次存入資料庫。
  • 最小進化等級解析:

    當你打上https://pokeapi.co/api/v2/evolution-chain/1/ 這隻api,他會出現以下結果:

    {
        "baby_trigger_item": null,
        "chain": {
            "evolution_details": [],
            "evolves_to": [
                {
                    "evolution_details": [
                        {
                            "gender": null,
                            "held_item": null,
                            "item": null,
                            "known_move": null,
                            "known_move_type": null,
                            "location": null,
                            "min_affection": null,
                            "min_beauty": null,
                            "min_happiness": null,
                            "min_level": 16,
                            "needs_overworld_rain": false,
                            "party_species": null,
                            "party_type": null,
                            "relative_physical_stats": null,
                            "time_of_day": "",
                            "trade_species": null,
                            "trigger": {
                                "name": "level-up",
                                "url": "https://pokeapi.co/api/v2/evolution-trigger/1/"
                            },
                            "turn_upside_down": false
                        }
                    ],
                    "evolves_to": [
                        {
                            "evolution_details": [
                                {
                                    "gender": null,
                                    "held_item": null,
                                    "item": null,
                                    "known_move": null,
                                    "known_move_type": null,
                                    "location": null,
                                    "min_affection": null,
                                    "min_beauty": null,
                                    "min_happiness": null,
                                    "min_level": 32,
                                    "needs_overworld_rain": false,
                                    "party_species": null,
                                    "party_type": null,
                                    "relative_physical_stats": null,
                                    "time_of_day": "",
                                    "trade_species": null,
                                    "trigger": {
                                        "name": "level-up",
                                        "url": "https://pokeapi.co/api/v2/evolution-trigger/1/"
                                    },
                                    "turn_upside_down": false
                                }
                            ],
                            "evolves_to": [],
                            "is_baby": false,
                            "species": {
                                "name": "venusaur",
                                "url": "https://pokeapi.co/api/v2/pokemon-species/3/"
                            }
                        }
                    ],
                    "is_baby": false,
                    "species": {
                        "name": "ivysaur",
                        "url": "https://pokeapi.co/api/v2/pokemon-species/2/"
                    }
                }
            ],
            "is_baby": false,
            "species": {
                "name": "bulbasaur",
                "url": "https://pokeapi.co/api/v2/pokemon-species/1/"
            }
        },
        "id": 1
    }
    

    也就是他每一個進化鏈其實是包含從最初還未進化的寶可夢一直到進化的最終形態,會一直都包含在同一個進化鏈裡面。

    • 解析過程

      public function getEvolutionMinLevel($chain, $pokemonName)
          {
              // 如果是第一階段的寶可夢
              if ($chain['chain']['species']['name'] == $pokemonName) {
                  // 查看它的進化細節,找到進化等級
                  if (isset($chain['chain']['evolves_to'][0]['evolution_details'][0]['min_level'])) {
                      return $chain['chain']['evolves_to'][0]['evolution_details'][0]['min_level'];
                  }
      
                  // 代表他不用進化
                  return null;
              }
              return $this->searchEvolutionLevel($chain['chain'], $pokemonName);
          }
      
          public function searchEvolutionLevel($chain, $pokemonName)
          {
              if ($chain['species']['name'] == $pokemonName) {
                  if (isset($chain['evolves_to'][0]['evolution_details'][0]['min_level'])) {
                      return $chain['evolves_to'][0]['evolution_details'][0]['min_level'];
                  }
                  // 可能代表此為最高級
                  return null;
              }
      
              // 重複以上過程直到找到該寶可夢
              foreach ($chain['evolves_to'] as $nextChain) {
                  $level = $this->searchEvolutionLevel($nextChain, $pokemonName);
                  if ($level !== null) {
                      return $level;
                  }
              }
      
              return null;
          }
      
      • 其實簡單來說第一個函數的作用是,如果今天是最一開始的型態的寶可夢(比如妙蛙種子)或是他本來就沒有進化,我就可以直接取得資料。
      • 如果今天是下一階段的進化,則會進入第二個函式,如果他是最終進化形態那就會在foreach的地方進入下一層然後回call自己再跑一次。

小結語

雖然資料是取得了但其實還有一些問題:

定時更新

因為我是透過在一開始去第三方取得所有資料的方式,所以我必須考慮到,跟第三方的同步問題,這個部分的設定我會在下一章分享。

改善效率

想當然爾, 我剛剛那種發送方式, 一個循環會發送3次api, 而我有幾隻寶可夢就是乘以3, 所以總共是3000多次, 然後再加上我每次存入資料庫的時間, 我每做一次這樣的資料的取得, 差不多會花個30~40 分鐘。

對於如何改善,後續有時間我再來研究。


上一篇
Day16:寶可夢專案-寶可夢資料庫的建立-寶可夢資料的儲存-本地or不存
下一篇
Day18:寶可夢專案-寶可夢資料庫的建立-請求大量資料的問題與改善-定時更新資料
系列文
Laravel專案練習-寶可夢管理系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言